/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
 */

import process from 'process';

import { Command, program } from 'commander';
import * as log4js from 'log4js';

import { exit } from '../../base/boot/hooks/exit.js';
import { configureDaemonClient } from '../../base/daemon/log/hvigor-daemon-client-log.js';
import { logDaemonInfo, stopDaemons } from '../../base/daemon/util/process-info-util.js';
import { coreParameter } from '../../base/internal/data/global-core-parameters.js';
import { initStartData } from '../../base/internal/data/global-data.js';
import baseLog from '../../base/log/base-log.js';
import { getConfiguration } from '../../base/log/default-configuration.js';
import { evaluateLogLevel, HvigorLogger } from '../../base/log/hvigor-log.js';
import { checkHvigorConfigBeforeProgram } from '../../base/util/check-hvigor-config-before-program.js';
import { HvigorCliOptions } from '../../base/util/options/hvigor-cli-options.js';
import { initHook } from '../util/std-hook.js';
import { CUR_HVIGOR_VERSION } from '../wrapper/wrapper-const.js';

type HvigorCommand = Command & {
  flagPair: (this: HvigorCommand, flags: string, description: string) => HvigorCommand;
  deprecatedOption: (this: HvigorCommand, flags: string, recommendedFlags: string, description: string) => HvigorCommand;
};

const cliVersion = CUR_HVIGOR_VERSION;

const _log: HvigorLogger = HvigorLogger.getLogger('hvigor');

const hvigorCommandMixin = {
  flagPair(this: HvigorCommand, flags: string, description: string): HvigorCommand {
    return this.option(`--${flags}`, `Enables ${description}`).option(`--no-${flags}`, `Disables ${description}`);
  },
  deprecatedOption(this: HvigorCommand, flags: string, recommendedFlags: string, description: string): HvigorCommand {
    return this.option(flags, `['${flags}' deprecated: use '${recommendedFlags}' instead] ${description}`);
  },
};

Object.assign(Command.prototype, hvigorCommandMixin);

/**
 * 添加command类操作
 * @param result
 */
function addCommands(result: string[]) {
  program
    .command('version')
    .action(() => {
      _log.info('CLI version:', cliVersion);
      _log.info('Local version:', cliVersion || 'Unknown');
      exit(0);
    })
    .description('Shows the version of Hvigor.');

  program
    .command('tasks')
    .action(() => {
      result.unshift('tasks');
    })
    .description('Shows all available tasks of specific modules.');

  program
    .command('taskTree')
    .action(() => {
      result.unshift('taskTree');
    })
    .description('Shows all available task trees of specific modules.');

  program
    .command('prune')
    .action(() => {
      result.unshift('prune');
    })
    .description('Cleans up Hvigor cache files and removes unreferenced packages from store.');

  program
    .command('collectCoverage')
    .action(() => {
      result.unshift('collectCoverage');
    })
    .description('Generates coverage statistics reports based on instrumentation test data.');
}

/**
 * 添加基本命令行参数（option）
 */
function addBasicOptions() {
  program
    .option('-e, --error', 'Sets the log level to error.')
    .option('-w, --warn', 'Sets the log level to warn.')
    .option('-i, --info', 'Sets the log level to info.')
    .option('-d, --debug', 'Sets the log level to debug.')
    .option<string[]>(
      '-c, --config <config>',
      'Sets properties in the hvigor-config.json5 file. The settings will overwrite those in the file.',
      (value, previous = []) => previous.concat(value),
    )
    .option(
      '-p, --prop <value>',
      'Defines extra properties.',
      (value: string, previous: string[]) => {
        return previous.concat([value]);
      },
      [],
    )
    .option('-m, --mode <string>', 'Sets the mode in which the command is executed.')
    .option('-s, --sync', 'Syncs the information in plugin for other platforms.')
    .option('--node-home, <string>', 'Sets the Node.js location.')
    .option('--stop-daemon', 'Stops the current project\'s daemon process.')
    .option('--stop-daemon-all', 'Stops all projects\' daemon process.')
    .option('--status-daemon', 'Shows the daemon process status of the current project.')
    .option('--verbose-analyze', 'Enables detailed mode for build analysis.')
    .option('--watch', 'Enables watch mode.')
    .option('--hot-compile', 'HotReload watch mode to compile.')
    .option('--hot-reload-build', 'HotReload build')
    .option('--max-old-space-size <integer>', 'Sets the maximum memory size of V8\'s old memory section.', (value) => {
      const num = Number(value);
      if (!Number.isInteger(num) || num <= 0) {
        _log.printErrorExit('INVALID_MAX_OLD_SPACE_SIZE', [value]);
      }
      return num;
    })
    .option('--max-semi-space-size <integer>', 'Sets the maximum memory size of V8\'s new space memory section.', (value) => {
      const num = Number(value);
      if (!Number.isInteger(num) || num <= 0) {
        _log.warn(`MaxSemiSpaceSize must be positive integer. --max-semi-space-size=${value} is invalid. Default value will be used by Node.`);
      }
      return num;
    });
}

/**
 * 添加额外命令行参数
 */
function addOtherOptions() {
  (program as HvigorCommand)
    .option('--Xmx <integer>', 'Sets the maximum JVM heap size, in MB.', (value) => {
      const num = Number(value);
      if (!Number.isInteger(num) || num <= 0) {
        throw new Error('--Xmx should be positive integer.');
      }
      return num;
    })
    .option('--optimization-strategy <string>', 'Sets the optimization strategy: memory, performance.')
    .deprecatedOption(
      '--enable-build-script-type-check',
      'type-check',
      'Enables the build script hvigorfile.ts type check. This option is deprecated. Use \'type-check\' instead.',
    )
    .flagPair('stacktrace', 'the printing of stack traces for all exceptions.')
    .flagPair('type-check', 'the build script hvigorfile.ts type check.')
    .flagPair('parallel', 'parallel building mode.')
    .flagPair('incremental', 'incremental building mode.')
    .flagPair('daemon', 'building with daemon process.')
    .flagPair('generate-build-profile', 'the generation of BuildProfile.ets files.')
    .flagPair('analyze', 'build analysis.')
    .option('--analyze=<analysisMode>', 'Sets the build analysis mode: normal (default), advanced, false.')
    .addHelpText('after', '\nExamples:\n  hvigor assembleApp  Do assembleApp task\n')
    .addHelpText('after', 'copyright 2023')
    .helpOption('-h, --help', 'Displays help information.')
    .allowUnknownOption();
}

/**
 * 解析hvigor命令行参数
 *
 * @return {HvigorCliOptions} 解析后的命令行参数
 */
export function parseCommand(): HvigorCliOptions {
  initHook();
  let result: string[] = [];

  // cli options定义所有的可支持的命令行，解析命令行
  (program.version(cliVersion, '-v, --version', 'Shows the version of Hvigor.') as HvigorCommand).name('hvigor').usage('[taskNames...] <options...>');
  addBasicOptions();
  addOtherOptions();
  program.arguments('[taskNames...]').action((taskNames) => (result = taskNames));

  addCommands(result);
  program.parse(process.argv);

  const cliArgv: HvigorCliOptions = {
    ...program.opts(),
    _: result,
    env: process.env
  } as HvigorCliOptions;

  // 此处对hvigor-config文件做一次校验，这里时机比较特殊，在这里刚好读取完命令行参数，同时对log进行了时间截断处理。整个程序的报错是被统一包装起来的，但在此时还未走的程序运行内部，需要独立做一次抛错处理。
  // 暂时未想到较好的解决方案，对hvigor-config文件的依赖，耦合太深，改动太大，留待后续整体整改处理
  if (!checkHvigorConfigBeforeProgram(cliArgv, baseLog).result) {
    exit(-1);
  }

  initStartData(cliArgv);

  // 设置日志级别
  initLogger();
  evaluateLogLevel(coreParameter.startParams.logLevel);
  return cliArgv;
}

/**
 * 在确定使用daemon的情况下，在这里初始化一次daemonClientLogger
 * logger只需要初始化一次configuration就可以了，其实是不用初始化很多次的
 */
function initDaemonClientLogger() {
  configureDaemonClient();
  evaluateLogLevel(coreParameter.startParams.logLevel);
}

function initLogger() {
  log4js.shutdown();
  log4js.configure(getConfiguration());
}

/**
 * hvigor执行入口，根据命令行参数是否启用服务化调用不同的启动接口。
 *
 * @param {HvigorCliOptions} cliArgv hvigor命令行参数
 */
export async function startHvigorBuild(cliArgv: HvigorCliOptions) {
  if (cliArgv.stopDaemon) {
    stopDaemons(false);
  }

  if (cliArgv.stopDaemonAll) {
    stopDaemons(true);
  }

  if (cliArgv.statusDaemon) {
    logDaemonInfo();
  }

  // true || undefined: daemon
  // false: no daemon
  _log.debug(`env: daemon=${coreParameter.startParams.daemon}`);
  if (coreParameter.startParams.daemon) {
    // 在这里可以确保要用client了
    initDaemonClientLogger();
    const { daemonBoot } = require('./daemon-client.js');
    await daemonBoot(cliArgv);
  } else {
    process.execArgv.length > 0
      ? _log.debug(`no-daemon, use the parent process.execArgv ${process.execArgv}`)
      : _log.debug('no-daemon, use the default node options');
    const { boot } = require('../../base/boot/index.js');
    await boot(cliArgv);
  }
}
